package org.study.mybatisplus.dynamic.boot.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.idev.excel.EasyExcel;
import cn.idev.excel.read.listener.PageReadListener;
import cn.idev.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.*;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.study.mybatisplus.dynamic.boot.config.SystemConstant;
import org.study.mybatisplus.dynamic.boot.dto.*;
import org.study.mybatisplus.dynamic.boot.mapper.*;
import org.study.mybatisplus.dynamic.boot.model.*;
import org.study.mybatisplus.dynamic.boot.service.DeviceService;
import org.study.mybatisplus.dynamic.boot.util.TDengineUtils.DateTypeEnum;
import org.study.mybatisplus.dynamic.boot.vo.*;

import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 设备管理
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2024年10月08日
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class DeviceServiceImpl implements DeviceService {
    private final IotDeviceMapper iotDeviceMapper;
    private final IotDevicePropertyMapper iotDevicePropertyMapper;
    private final IotAlarmConfigMapper iotAlarmConfigMapper;
    private final IotDatasourceMapper iotDatasourceMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deviceSave(DeviceSaveDTO deviceSaveDTO) {
        LocalDateTime now = LocalDateTime.now();
        IotDevice iotDevice = new IotDevice();
        iotDevice.setProjectId(deviceSaveDTO.getProjectId());
        iotDevice.setPowerStationId(deviceSaveDTO.getPowerStationId());
        iotDevice.setGatewayNo(deviceSaveDTO.getGatewayNo());
        iotDevice.setDeviceNo(deviceSaveDTO.getDeviceNo());
        iotDevice.setDeviceName(deviceSaveDTO.getDeviceName());
        iotDevice.setDeviceInfo(deviceSaveDTO.getDeviceInfo());
        iotDevice.setDeviceConfig(deviceSaveDTO.getDeviceConfig());
        if (null == deviceSaveDTO.getDeviceId()) {
            // 设备编号不能重复
            /*Long deviceNoCount = iotDeviceMapper.selectCount(
                    Wrappers.<IotDevice>query().lambda()
                            .eq(IotDevice::getProjectId, deviceSaveDTO.getProjectId())
                            .eq(IotDevice::getGatewayNo, deviceSaveDTO.getGatewayNo())
                            .eq(IotDevice::getDeviceNo, deviceSaveDTO.getDeviceNo())
            );
            if (0L != deviceNoCount) {
                throw new RuntimeException("当前设备编号已存在");
            }*/
            // 设备名称不能重复
            Long deviceNameCount = iotDeviceMapper.selectCount(
                    Wrappers.<IotDevice>query().lambda()
                            .eq(IotDevice::getProjectId, deviceSaveDTO.getProjectId())
                            .eq(IotDevice::getGatewayNo, deviceSaveDTO.getGatewayNo())
                            .eq(IotDevice::getDeviceName, deviceSaveDTO.getDeviceName())
            );
            if (0L != deviceNameCount) {
                throw new RuntimeException("当前设备名称已存在");
            }
            // 新增
            iotDevice.setId(IdUtil.getSnowflakeNextId());
            iotDevice.setTenantId("000000");
            iotDevice.setStatus(SystemConstant.DB_STATUS_NORMAL);
            iotDevice.setIsDeleted(SystemConstant.DB_NOT_DELETED);
            iotDevice.setCreateUserId(202110081530000L);
            iotDevice.setCreateUserName("个人测试");
            iotDevice.setCreateTime(now);
            iotDeviceMapper.insert(iotDevice);
            try {
                // 动态数据源切换
                DynamicDataSourceContextHolder.push(SystemConstant.DS_TDENGINE);
                DynamicDataSourceContextHolder.peek();
                // 创建日志表
                iotDatasourceMapper.createLogTable("log_" + iotDevice.getProjectId() + "_" + iotDevice.getId(), "设备日志_" + iotDevice.getId());
            } catch (Exception ex) {
                log.error("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败", ex);
                throw new RuntimeException("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败");
            } finally {
                DynamicDataSourceContextHolder.clear();
            }
        } else {
            // 设备编号不能重复
            /*Long deviceNoCount = iotDeviceMapper.selectCount(
                    Wrappers.<IotDevice>query().lambda()
                            .eq(IotDevice::getProjectId, deviceSaveDTO.getProjectId())
                            .eq(IotDevice::getGatewayNo, deviceSaveDTO.getGatewayNo())
                            .eq(IotDevice::getDeviceNo, deviceSaveDTO.getDeviceNo())
                            .ne(IotDevice::getId, deviceSaveDTO.getDeviceId())
            );
            if (0L != deviceNoCount) {
                throw new RuntimeException("当前设备编号已存在");
            }*/
            // 设备名称不能重复
            Long deviceNameCount = iotDeviceMapper.selectCount(
                    Wrappers.<IotDevice>query().lambda()
                            .eq(IotDevice::getProjectId, deviceSaveDTO.getProjectId())
                            .eq(IotDevice::getGatewayNo, deviceSaveDTO.getGatewayNo())
                            .eq(IotDevice::getDeviceName, deviceSaveDTO.getDeviceName())
                            .ne(IotDevice::getId, deviceSaveDTO.getDeviceId())
            );
            if (0L != deviceNameCount) {
                throw new RuntimeException("当前设备名称已存在");
            }
            // 修改
            iotDevice.setId(deviceSaveDTO.getDeviceId());
            iotDevice.setUpdateUserId(202110081530000L);
            iotDevice.setUpdateUserName("个人测试");
            iotDevice.setUpdateTime(now);
            iotDeviceMapper.updateById(iotDevice);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deviceRemove(long id) {
        LocalDateTime now = LocalDateTime.now();
        IotDevice iotDevice = iotDeviceMapper.selectById(id);
        if (null == iotDevice || SystemConstant.DB_IS_DELETED == iotDevice.getIsDeleted()) {
            throw new RuntimeException("当前设备不存在或已删除");
        }
        IotDevice deleteIotDevice = new IotDevice();
        deleteIotDevice.setId(id);
        deleteIotDevice.setIsDeleted(SystemConstant.DB_IS_DELETED);
        deleteIotDevice.setUpdateUserId(202110081530000L);
        deleteIotDevice.setUpdateUserName("个人测试");
        deleteIotDevice.setUpdateTime(now);
        iotDeviceMapper.updateById(deleteIotDevice);

        List<String> tableList = iotDevicePropertyMapper.selectList(
                        Wrappers.<IotDeviceProperty>query().lambda()
                                .select(IotDeviceProperty::getPropertyTable)
                                .eq(IotDeviceProperty::getDeviceId, id)
                                .eq(IotDeviceProperty::getIsDeleted, SystemConstant.DB_NOT_DELETED)
                )
                .stream()
                .map(IotDeviceProperty::getPropertyTable)
                .distinct()
                .collect(Collectors.toList());
        // 删除设备下的点位
        IotDeviceProperty iotDeviceProperty = new IotDeviceProperty();
        iotDeviceProperty.setIsDeleted(SystemConstant.DB_IS_DELETED);
        iotDeviceProperty.setUpdateUserId(202110081530000L);
        iotDeviceProperty.setUpdateUserName("个人测试");
        iotDeviceProperty.setUpdateTime(now);
        iotDevicePropertyMapper.update(iotDeviceProperty,
                new LambdaUpdateWrapper<IotDeviceProperty>()
                        .eq(IotDeviceProperty::getDeviceId, id)
                        .eq(IotDeviceProperty::getIsDeleted, SystemConstant.DB_NOT_DELETED)
        );
        try {
            // 动态数据源切换
            DynamicDataSourceContextHolder.push(SystemConstant.DS_TDENGINE);
            DynamicDataSourceContextHolder.peek();
            // 删除设备的TDengine表
            tableList.forEach(iotDatasourceMapper::dropTable);
            // 删除日志表
            String logTableName = "log_" + iotDevice.getProjectId() + "_" + iotDevice.getId();
            iotDatasourceMapper.dropTable(logTableName);
        } catch (Exception ex) {
            log.error("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败", ex);
            throw new RuntimeException("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败");
        } finally {
            DynamicDataSourceContextHolder.clear();
        }
        return true;
    }

    @Override
    public IPage<DeviceVO> deviceList(Page<DeviceVO> page, Long projectId, String deviceName) {
        return iotDeviceMapper.selectDeviceList(page, projectId, deviceName);
    }

    @Override
    public DeviceVO deviceDetail(Long deviceId) {
        return iotDeviceMapper.selectDeviceDetail(deviceId);
    }

    @Override
    public boolean property2Tsdb1(Long deviceId) {
        List<IotDevice> iotDeviceList = iotDeviceMapper.selectList(
                Wrappers.<IotDevice>query().lambda()
                        .eq(IotDevice::getIsDeleted, SystemConstant.DB_NOT_DELETED)
                        .eq(deviceId != null, IotDevice::getId, deviceId)
        );
        iotDeviceList.forEach(iotDevice -> {
            Long currentDeviceId = iotDevice.getId();
            Long currentProjectId = iotDevice.getProjectId();
            List<IotDeviceProperty> deviceInstancePropertyList = iotDevicePropertyMapper.selectList(
                    Wrappers.<IotDeviceProperty>query().lambda()
                            .eq(IotDeviceProperty::getIsDeleted, SystemConstant.DB_NOT_DELETED)
                            .eq(IotDeviceProperty::getDeviceId, currentDeviceId)
            );
            try {
                // 动态数据源切换
                DynamicDataSourceContextHolder.push(SystemConstant.DS_TDENGINE);
                DynamicDataSourceContextHolder.peek();
                String logTableName = "log_" + currentProjectId + "_" + currentDeviceId;
                String propertyTableName = "property_" + currentProjectId + "_" + currentDeviceId + "_0";
                // 删除旧数据
                iotDatasourceMapper.dropTable(logTableName);
                iotDatasourceMapper.dropTable(propertyTableName);
                // 创建设备日志表
                iotDatasourceMapper.createLogTable(logTableName, "设备日志_" + currentDeviceId);
                // 创建设备属性表
                iotDatasourceMapper.createPropertyTable(propertyTableName, "设备数据_" + currentDeviceId);
                if (CollectionUtils.isNotEmpty(deviceInstancePropertyList)) {
                    // 批量插入时序库
                    deviceInstancePropertyList.forEach(deviceInstanceProperty -> {
                        // iotDatasourceMapper.addColumn(propertyTableName, deviceInstanceProperty.getPropertyNo(), this.convertPropertyType(deviceInstanceProperty.getPropertyType(), String.valueOf(deviceInstanceProperty.getPropertyScale())))
                        if (DateTypeEnum.BINARY.getDateType().equalsIgnoreCase(deviceInstanceProperty.getPropertyType()) ||
                                DateTypeEnum.VARCHAR.getDateType().equalsIgnoreCase(deviceInstanceProperty.getPropertyType()) ||
                                DateTypeEnum.VARBINARY.getDateType().equalsIgnoreCase(deviceInstanceProperty.getPropertyType()) ||
                                DateTypeEnum.NCHAR.getDateType().equalsIgnoreCase(deviceInstanceProperty.getPropertyType())) {
                            iotDatasourceMapper.addColumnNchar(propertyTableName, deviceInstanceProperty.getPropertyNo(), deviceInstanceProperty.getPropertyType(), deviceInstanceProperty.getPropertyScale());
                        } else if (DateTypeEnum.DECIMAL.getDateType().equalsIgnoreCase(deviceInstanceProperty.getPropertyType())) {
                            iotDatasourceMapper.addColumnDecimal(propertyTableName, deviceInstanceProperty.getPropertyNo(), deviceInstanceProperty.getPropertyType(), 18, 6);
                        } else {
                            iotDatasourceMapper.addColumn(propertyTableName, deviceInstanceProperty.getPropertyNo(), deviceInstanceProperty.getPropertyType());
                        }
                    });
                }
            } catch (Exception ex) {
                log.error("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败", ex);
                throw new RuntimeException("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败");
            } finally {
                DynamicDataSourceContextHolder.clear();
            }
        });
        return true;
    }

    @Override
    public boolean property2Tsdb2(Long deviceId) {
        List<IotDevice> iotDeviceList = iotDeviceMapper.selectList(
                Wrappers.<IotDevice>query().lambda()
                        .eq(IotDevice::getIsDeleted, SystemConstant.DB_NOT_DELETED)
                        .eq(deviceId != null, IotDevice::getId, deviceId)
        );
        iotDeviceList.forEach(iotDevice -> {
            Long currentDeviceId = iotDevice.getId();
            Long currentProjectId = iotDevice.getProjectId();
            List<IotDeviceProperty> deviceInstancePropertyList = iotDevicePropertyMapper.selectList(
                    Wrappers.<IotDeviceProperty>query().lambda()
                            .eq(IotDeviceProperty::getIsDeleted, SystemConstant.DB_NOT_DELETED)
                            .eq(IotDeviceProperty::getDeviceId, currentDeviceId)
            );
            try {
                // 动态数据源切换
                DynamicDataSourceContextHolder.push(SystemConstant.DS_TDENGINE);
                DynamicDataSourceContextHolder.peek();
                String logTableName = "log_" + currentProjectId + "_" + currentDeviceId;
                String propertyTableName = "property_" + currentProjectId + "_" + currentDeviceId + "_0";
                // 删除旧数据
                iotDatasourceMapper.dropTable(logTableName);
                iotDatasourceMapper.dropTable(propertyTableName);
                StringBuffer sql = new StringBuffer("CREATE TABLE IF NOT EXISTS " + propertyTableName + "(");
                sql.append("`time` timestamp,")
                        .append("`gatewayNo` BINARY(32),")
                        .append("`deviceNo` BINARY(32)");
                StringBuffer fieldSql = new StringBuffer();
                if (CollectionUtils.isNotEmpty(deviceInstancePropertyList)) {
                    fieldSql.append(",");
                    // 批量插入时序库
                    deviceInstancePropertyList.forEach(deviceInstanceProperty -> {
                        if (DateTypeEnum.BINARY.getDateType().equalsIgnoreCase(deviceInstanceProperty.getPropertyType()) ||
                                DateTypeEnum.VARCHAR.getDateType().equalsIgnoreCase(deviceInstanceProperty.getPropertyType()) ||
                                DateTypeEnum.VARBINARY.getDateType().equalsIgnoreCase(deviceInstanceProperty.getPropertyType()) ||
                                DateTypeEnum.NCHAR.getDateType().equalsIgnoreCase(deviceInstanceProperty.getPropertyType())) {
                            fieldSql.append("`").append(deviceInstanceProperty.getPropertyNo()).append("` ")
                                    .append(deviceInstanceProperty.getPropertyType())
                                    .append("(").append(deviceInstanceProperty.getPropertyScale()).append(")")
                                    .append(",");
                        } else if (DateTypeEnum.DECIMAL.getDateType().equalsIgnoreCase(deviceInstanceProperty.getPropertyType())) {
                            fieldSql.append("`").append(deviceInstanceProperty.getPropertyNo()).append("` ")
                                    .append(deviceInstanceProperty.getPropertyType())
                                    .append("(18, 6)")
                                    .append(",");
                        } else {
                            fieldSql.append("`").append(deviceInstanceProperty.getPropertyNo()).append("` ")
                                    .append(deviceInstanceProperty.getPropertyType())
                                    .append(",");
                        }
                    });
                }
                if (StringUtils.isNotBlank(fieldSql)) {
                    // 去掉末尾的逗号
                    sql.append(fieldSql.substring(0, fieldSql.lastIndexOf(",")));
                }
                sql.append(") COMMENT '设备数据_" + currentDeviceId + "'");
                // 创建设备日志表
                iotDatasourceMapper.createLogTable(logTableName, "设备日志_" + currentDeviceId);
                // 创建设备属性表
                iotDatasourceMapper.executeSql(sql.toString());
            } catch (Exception ex) {
                log.error("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败", ex);
                throw new RuntimeException("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败");
            } finally {
                DynamicDataSourceContextHolder.clear();
            }
        });
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean devicePropertySave(DevicePropertySaveDTO devicePropertySaveDTO) {
        LocalDateTime now = LocalDateTime.now();
        IotDevice iotDevice = iotDeviceMapper.selectById(devicePropertySaveDTO.getDeviceId());
        if (null == iotDevice) {
            throw new RuntimeException("当前设备不存在");
        }
        IotDeviceProperty iotDeviceProperty = new IotDeviceProperty();
        iotDeviceProperty.setPropertyName(devicePropertySaveDTO.getPropertyName());
        iotDeviceProperty.setPropertyScale(devicePropertySaveDTO.getPropertyScale());
        iotDeviceProperty.setPropertyUnit(devicePropertySaveDTO.getPropertyUnit());
        if (null == devicePropertySaveDTO.getPropertyId()) {
            Long propertyNoCount = iotDevicePropertyMapper.selectCount(
                    Wrappers.<IotDeviceProperty>query().lambda()
                            .eq(IotDeviceProperty::getProjectId, devicePropertySaveDTO.getProjectId())
                            .eq(IotDeviceProperty::getDeviceId, devicePropertySaveDTO.getDeviceId())
                            .eq(IotDeviceProperty::getPropertyNo, devicePropertySaveDTO.getPropertyNo())
            );
            if (0L != propertyNoCount) {
                throw new RuntimeException("当前设备下的该属性编号已存在");
            }
            // 查询当前该设备已有的属性
            Long existCount = iotDevicePropertyMapper.selectCount(
                    Wrappers.<IotDeviceProperty>query().lambda()
                            .eq(IotDeviceProperty::getDeviceId, devicePropertySaveDTO.getDeviceId())
                            .eq(IotDeviceProperty::getIsDeleted, SystemConstant.DB_NOT_DELETED)
            );
            String tableName = "property_" + devicePropertySaveDTO.getProjectId() + "_" + devicePropertySaveDTO.getDeviceId();
            // 查询项目绑定的数据源
           /* IotProject iotProject = iotProjectMapper.selectById(devicePropertySaveDTO.getProjectId());
            if (null == iotProject || SystemConstant.DB_IS_DELETED == iotProject.getIsDeleted()) {
                throw new RuntimeException("当前项目不存在或已删除");
            }
            if (null == iotProject.getDatasourceId() || 0L == iotProject.getDatasourceId()) {
                throw new RuntimeException("当前设备所属项目尚未绑定数据源，请先前往【项目管理】绑定数据源");
            }
            IotDatasource iotDatasource = iotDatasourceMapper.selectById(iotProject.getDatasourceId());
            if (iotDatasource.getDatabaseType() != 2) {
                throw new RuntimeException("请为当前项目绑定时序数据库");
            }*/
            try {
                // 动态数据源切换
                DynamicDataSourceContextHolder.push(SystemConstant.DS_TDENGINE);
                DynamicDataSourceContextHolder.peek();
                // 查询表是否存在
                List<String> tableList = iotDatasourceMapper.showTables();
                if (!tableList.contains(tableName)) {
                    // 表不存在，先建表
                    iotDatasourceMapper.createTable(tableName, iotDevice.getGatewayNo(), iotDevice.getDeviceNo());
                }
                // 添加字段
                if (DateTypeEnum.BINARY.getDateType().equalsIgnoreCase(devicePropertySaveDTO.getPropertyType()) ||
                        DateTypeEnum.VARCHAR.getDateType().equalsIgnoreCase(devicePropertySaveDTO.getPropertyType()) ||
                        DateTypeEnum.VARBINARY.getDateType().equalsIgnoreCase(devicePropertySaveDTO.getPropertyType()) ||
                        DateTypeEnum.NCHAR.getDateType().equalsIgnoreCase(devicePropertySaveDTO.getPropertyType())) {
                    iotDatasourceMapper.addColumnNchar(tableName, devicePropertySaveDTO.getPropertyNo(), devicePropertySaveDTO.getPropertyType(), devicePropertySaveDTO.getPropertyScale());
                } else if (DateTypeEnum.DECIMAL.getDateType().equalsIgnoreCase(devicePropertySaveDTO.getPropertyType())) {
                    iotDatasourceMapper.addColumnDecimal(tableName, devicePropertySaveDTO.getPropertyNo(), devicePropertySaveDTO.getPropertyType(), 18, 6);
                } else {
                    iotDatasourceMapper.addColumn(tableName, devicePropertySaveDTO.getPropertyNo(), devicePropertySaveDTO.getPropertyType());
                }
            } catch (Exception ex) {
                log.error("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败", ex);
                throw new RuntimeException("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败");
            } finally {
                DynamicDataSourceContextHolder.clear();
            }
            // 新增
            iotDeviceProperty.setId(IdUtil.getSnowflakeNextId());
            iotDeviceProperty.setProjectId(devicePropertySaveDTO.getProjectId());
            iotDeviceProperty.setDeviceId(devicePropertySaveDTO.getDeviceId());
            iotDeviceProperty.setPropertyNo(devicePropertySaveDTO.getPropertyNo());
            iotDeviceProperty.setPropertyType(devicePropertySaveDTO.getPropertyType());
            iotDeviceProperty.setPropertyTable(tableName);
            iotDeviceProperty.setTenantId("000000");
            iotDeviceProperty.setStatus(SystemConstant.DB_STATUS_NORMAL);
            iotDeviceProperty.setIsDeleted(SystemConstant.DB_NOT_DELETED);
            iotDeviceProperty.setCreateUserId(202110081530000L);
            iotDeviceProperty.setCreateUserName("个人测试");
            iotDeviceProperty.setCreateTime(now);
            iotDevicePropertyMapper.insert(iotDeviceProperty);
        } else {
            Long propertyNoCount = iotDevicePropertyMapper.selectCount(
                    Wrappers.<IotDeviceProperty>query().lambda()
                            .eq(IotDeviceProperty::getProjectId, devicePropertySaveDTO.getProjectId())
                            .eq(IotDeviceProperty::getDeviceId, devicePropertySaveDTO.getDeviceId())
                            .eq(IotDeviceProperty::getPropertyNo, devicePropertySaveDTO.getPropertyNo())
                            .ne(IotDeviceProperty::getId, devicePropertySaveDTO.getPropertyId())
            );
            if (0L != propertyNoCount) {
                throw new RuntimeException("当前设备下的该属性编号已存在");
            }
            // 修改
            iotDeviceProperty.setId(devicePropertySaveDTO.getPropertyId());
            iotDeviceProperty.setUpdateUserId(202110081530000L);
            iotDeviceProperty.setUpdateUserName("个人测试");
            iotDeviceProperty.setUpdateTime(now);
            iotDevicePropertyMapper.updateById(iotDeviceProperty);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean devicePropertyRemove(IdAuthDTO idAuthDTO) {
        IotDeviceProperty currentProperty = iotDevicePropertyMapper.selectById(idAuthDTO.getId());
        if (null == currentProperty || currentProperty.getIsDeleted() == SystemConstant.DB_IS_DELETED) {
            throw new RuntimeException("当前属性不存在或已删除");
        }
        try {
            // 动态数据源切换
            DynamicDataSourceContextHolder.push(SystemConstant.DS_TDENGINE);
            DynamicDataSourceContextHolder.peek();
            iotDatasourceMapper.dropColumn(currentProperty.getPropertyTable(), currentProperty.getPropertyNo());
        } catch (Exception ex) {
            log.error("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败", ex);
            throw new RuntimeException("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败");
        } finally {
            DynamicDataSourceContextHolder.clear();
        }
        // 删除属性
        LocalDateTime now = LocalDateTime.now();
        IotDeviceProperty iotDeviceProperty = new IotDeviceProperty();
        iotDeviceProperty.setId(idAuthDTO.getId());
        iotDeviceProperty.setIsDeleted(SystemConstant.DB_IS_DELETED);
        iotDeviceProperty.setUpdateUserId(202110081530000L);
        iotDeviceProperty.setUpdateUserName("个人测试");
        iotDeviceProperty.setUpdateTime(now);
        iotDevicePropertyMapper.updateById(iotDeviceProperty);
        return true;
    }

    @Override
    public IPage<DevicePropertyVO> devicePropertyList(Page<DevicePropertyVO> page, Long deviceId, String propertyName) {
        return iotDevicePropertyMapper.selectDevicePropertyList(page, deviceId, propertyName);
    }

    @Override
    public boolean deviceImport(Long projectId, Long powerStationId, MultipartFile excelFile) {
        LocalDateTime now = LocalDateTime.now();
        List<PropertyImportDTO> importList = Lists.newArrayList();
        try {
            // 采用fastexcel
            EasyExcel.read(excelFile.getInputStream(), PropertyImportDTO.class, new PageReadListener<PropertyImportDTO>(importList::addAll))
                    .sheet()
                    .doRead();
            // 采用hutool
            ExcelReader excelReader = ExcelUtil.getReader(excelFile.getInputStream());
            excelReader.addHeaderAlias("网关编号", "gatewayNo");
            excelReader.addHeaderAlias("设备编号", "deviceNo");
            excelReader.addHeaderAlias("设备名称", "deviceName");
            excelReader.addHeaderAlias("属性编号", "propertyNo");
            excelReader.addHeaderAlias("属性名称", "propertyName");
            excelReader.addHeaderAlias("属性类型", "propertyType");
            excelReader.addHeaderAlias("属性精度", "propertyScale");
            excelReader.addHeaderAlias("属性单位", "propertyUnit");
            excelReader.addHeaderAlias("告警值", "alarmValue");
            List<PropertyImportDTO> hutoolImportList = excelReader.read(0, 1, PropertyImportDTO.class);
            log.info("解析Excel成功：采用fastexcel={}，采用hutool={}", importList, hutoolImportList);
        } catch (Exception ex) {
            log.error("一键导入设备及设备点位时异常", ex);
            throw new RuntimeException(ex);
        }
        if (CollectionUtils.isEmpty(importList)) {
            throw new RuntimeException("上传的Excel数据为空或格式不正确");
        }
        // Excel校验
        // 只允许导入同一个网关下的设备
        List<String> gatewayNoCount = importList.stream()
                .map(PropertyImportDTO::getGatewayNo)
                .distinct()
                .collect(Collectors.toList());
        if (gatewayNoCount.size() != 1) {
            throw new RuntimeException("一次只允许导入同一个网关下的设备");
        }
        // 根据设备分组
        Map<String, List<PropertyImportDTO>> deviceMap = importList.parallelStream()
                .collect(Collectors.groupingBy(PropertyImportDTO::getDeviceNo));
        // 校验同一个设备下的属性编号是否重复
        for (Map.Entry<String, List<PropertyImportDTO>> entry : deviceMap.entrySet()) {
            String deviceNo = entry.getKey();
            List<PropertyImportDTO> propertyList = entry.getValue();
            this.hasDuplicatePropertyNo(deviceNo, propertyList);
        }
        // 创建设备
        for (Map.Entry<String, List<PropertyImportDTO>> entry : deviceMap.entrySet()) {
            List<PropertyImportDTO> propertyList = entry.getValue();
            PropertyImportDTO propertyImportDTO = propertyList.get(0);
            Long deviceId = IdUtil.getSnowflakeNextId();
            IotDevice iotDevice = IotDevice.builder()
                    .id(deviceId)
                    .projectId(projectId)
                    .powerStationId(powerStationId)
                    .gatewayNo(propertyImportDTO.getGatewayNo())
                    .deviceNo(propertyImportDTO.getDeviceNo())
                    .deviceName(propertyImportDTO.getDeviceName())
                    .deviceInfo("")
                    .deviceConfig("")
                    .tenantId("000000")
                    .status(SystemConstant.DB_STATUS_NORMAL)
                    .isDeleted(SystemConstant.DB_NOT_DELETED)
                    .createUserId(202110081530000L)
                    .createUserName("个人测试")
                    .createTime(now)
                    .updateUserId(202110081530000L)
                    .updateUserName("个人测试")
                    .updateTime(now)
                    .build();
            iotDeviceMapper.insert(iotDevice);
            // 创建属性
            List<List<PropertyImportDTO>> partitionList = Lists.partition(propertyList, 3000);
            int split = 0;
            for (List<PropertyImportDTO> subPartition : partitionList) {
                List<CreateDeviceDTO> createDeviceDTOList = Lists.newArrayList();
                List<IotDeviceProperty> insertPropertyList = Lists.newArrayList();
                List<IotAlarmConfig> insertAlarmConfigList = Lists.newArrayList();
                String tableName = "property_" + projectId + "_" + deviceId + "_" + split;
                PropertyImportDTO property = subPartition.get(0);
                for (PropertyImportDTO partition : subPartition) {
                    IotDeviceProperty iotDeviceProperty = IotDeviceProperty.builder()
                            .id(IdUtil.getSnowflakeNextId())
                            .projectId(projectId)
                            .deviceId(deviceId)
                            .propertyNo(partition.getPropertyNo())
                            .propertyName(partition.getPropertyName())
                            .propertyType(partition.getPropertyType())
                            .propertyUnit(partition.getPropertyUnit())
                            .propertyTable(tableName)
                            .tenantId("000000")
                            .status(SystemConstant.DB_STATUS_NORMAL)
                            .isDeleted(SystemConstant.DB_NOT_DELETED)
                            .createUserId(202110081530000L)
                            .createUserName("个人测试")
                            .createTime(now)
                            .updateUserId(202110081530000L)
                            .updateUserName("个人测试")
                            .updateTime(now)
                            .build();
                    CreateDeviceDTO createDeviceDTO = CreateDeviceDTO.builder()
                            .propertyNo(partition.getPropertyNo())
                            .propertyType(partition.getPropertyType())
                            .build();
                    if (DateTypeEnum.BINARY.getDateType().equalsIgnoreCase(partition.getPropertyType()) ||
                            DateTypeEnum.VARCHAR.getDateType().equalsIgnoreCase(partition.getPropertyType()) ||
                            DateTypeEnum.VARBINARY.getDateType().equalsIgnoreCase(partition.getPropertyType()) ||
                            DateTypeEnum.NCHAR.getDateType().equalsIgnoreCase(partition.getPropertyType())) {
                        iotDeviceProperty.setPropertyScale(partition.getPropertyScale() == null ? 255 : partition.getPropertyScale());
                        createDeviceDTO.setPropertyScale(partition.getPropertyScale() == null ? "255" : String.valueOf(partition.getPropertyScale()));
                    } else if (DateTypeEnum.DECIMAL.getDateType().equalsIgnoreCase(partition.getPropertyType())) {
                        iotDeviceProperty.setPropertyScale(null);
                        createDeviceDTO.setPropertyScale("18, 6");
                    } else {
                        iotDeviceProperty.setPropertyScale(null);
                        createDeviceDTO.setPropertyScale(null);
                    }
                    insertPropertyList.add(iotDeviceProperty);
                    createDeviceDTOList.add(createDeviceDTO);
                    // 点位告警配置
                    BigDecimal alarmValue = partition.getAlarmValue();
                    if (alarmValue != null) {
                        AlarmTypePropertyDTO alarmTypePropertyDTO = AlarmTypePropertyDTO.builder()
                                .gatewayNo(iotDevice.getGatewayNo())
                                .deviceId(deviceId)
                                .deviceNo(iotDevice.getDeviceNo())
                                .propertyId(iotDeviceProperty.getId())
                                .propertyNo(iotDeviceProperty.getPropertyNo())
                                .propertyName(iotDeviceProperty.getPropertyName())
                                .lowerLimit(alarmValue.toPlainString())
                                .upperLimit(alarmValue.toPlainString())
                                .noticeThreshold(60 * 60)
                                .build();
                        IotAlarmConfig iotAlarmConfig = IotAlarmConfig.builder()
                                .alarmType(2)
                                .alarmName(partition.getPropertyName() + "告警")
                                .alarmNoticeType(1)
                                .alarmNoticeObject("")
                                .projectId(projectId)
                                .deviceId(iotDevice.getId())
                                .propertyId(iotDeviceProperty.getId())
                                .alarmConfig(JSONUtil.toJsonStr(alarmTypePropertyDTO))
                                .tenantId("000000")
                                .status(SystemConstant.DB_STATUS_NORMAL)
                                .isDeleted(SystemConstant.DB_NOT_DELETED)
                                .createUserId(202110081530000L)
                                .createUserName("个人测试")
                                .createTime(now)
                                .updateUserId(202110081530000L)
                                .updateUserName("个人测试")
                                .updateTime(now)
                                .build();
                        insertAlarmConfigList.add(iotAlarmConfig);
                    }
                }
                try {
                    // 动态数据源切换
                    DynamicDataSourceContextHolder.push(SystemConstant.DS_TDENGINE);
                    // 创建数据表
                    iotDatasourceMapper.createTableWithFiled(tableName, property.getGatewayNo(), property.getDeviceNo(), createDeviceDTOList);
                    // 创建日志表
                    iotDatasourceMapper.createLogTable("log_" + iotDevice.getProjectId() + "_" + iotDevice.getId(), "设备日志_" + iotDevice.getId());
                } catch (Exception ex) {
                    log.error("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败", ex);
                    throw new RuntimeException("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败");
                } finally {
                    DynamicDataSourceContextHolder.clear();
                }
                log.info("属性个数：{}，告警配置数;{}", insertPropertyList.size(), insertAlarmConfigList.size());
                // 属性入库
                iotDevicePropertyMapper.insert(insertPropertyList);
                // 属性告警配置入库
                iotAlarmConfigMapper.insert(insertAlarmConfigList);
                split++;
            }
        }
        log.info("导入设备及设备点位成功");
        return true;
    }

    @Override
    public boolean propertyImport(Long deviceId, MultipartFile excelFile) {
        LocalDateTime now = LocalDateTime.now();
        List<PropertyImportDTO> importList = Lists.newArrayList();
        try {
            // 采用fastexcel
            EasyExcel.read(excelFile.getInputStream(), PropertyImportDTO.class, new PageReadListener<PropertyImportDTO>(importList::addAll))
                    .sheet()
                    .doRead();
            // 采用hutool
            ExcelReader excelReader = ExcelUtil.getReader(excelFile.getInputStream());
            excelReader.addHeaderAlias("网关编号", "gatewayNo");
            excelReader.addHeaderAlias("设备编号", "deviceNo");
            excelReader.addHeaderAlias("设备名称", "deviceName");
            excelReader.addHeaderAlias("属性编号", "propertyNo");
            excelReader.addHeaderAlias("属性名称", "propertyName");
            excelReader.addHeaderAlias("属性类型", "propertyType");
            excelReader.addHeaderAlias("属性精度", "propertyScale");
            excelReader.addHeaderAlias("属性单位", "propertyUnit");
            excelReader.addHeaderAlias("告警值", "alarmValue");
            List<PropertyImportDTO> hutoolImportList = excelReader.read(0, 1, PropertyImportDTO.class);
            log.info("解析Excel成功：采用fastexcel={}，采用hutool={}", importList, hutoolImportList);
        } catch (Exception ex) {
            log.error("一键导入设备点位时异常", ex);
            throw new RuntimeException(ex);
        }
        if (CollectionUtils.isEmpty(importList)) {
            throw new RuntimeException("上传的Excel数据为空或格式不正确");
        }
        // 校验Excel中的属性编号是否重复
        List<String> excelPropertyNoList = Lists.newArrayList();
        Set<String> seenPropertyNoSet = Sets.newHashSet();
        importList.forEach(importDTO -> {
            // 检查属性列表中是否存在重复的属性编号。如果存在重复的属性编号，则返回true；否则返回false
            if (!seenPropertyNoSet.add(importDTO.getPropertyNo())) {
                throw new RuntimeException("上传的Excel表格中，属性名称为【" + importDTO.getPropertyName() + "】的属性编号【" + importDTO.getPropertyNo() + "】存在重复");
            }
            excelPropertyNoList.add(importDTO.getPropertyNo());
        });
        // 查询设备是否存在
        IotDevice iotDevice = iotDeviceMapper.selectById(deviceId);
        if (iotDevice == null) {
            throw new RuntimeException("id为" + deviceId + "的设备不存在");
        }
        Long projectId = iotDevice.getProjectId();
        // 校验数据库中的属性编号是否已存在
        List<IotDeviceProperty> existIotDevicePropertyList = iotDevicePropertyMapper.selectList(
                Wrappers.lambdaQuery(IotDeviceProperty.class)
                        .eq(IotDeviceProperty::getProjectId, projectId)
                        .eq(IotDeviceProperty::getDeviceId, deviceId)
                        .in(IotDeviceProperty::getPropertyNo, excelPropertyNoList)
        );
        if (CollectionUtils.isNotEmpty(existIotDevicePropertyList)) {
            List<String> existPropertyNoList = existIotDevicePropertyList.stream().map(IotDeviceProperty::getPropertyNo).collect(Collectors.toList());
            throw new RuntimeException("上传的Excel表格中，属性编号【" + existPropertyNoList + "】在数据库中已存在");
        }
        // 创建设备点位
        List<IotDeviceProperty> insertPropertyList = Lists.newArrayList();
        List<IotAlarmConfig> insertAlarmConfigList = Lists.newArrayList();
        String tableName = "property_" + projectId + "_" + deviceId + "_0";
        for (PropertyImportDTO partition : importList) {
            IotDeviceProperty iotDeviceProperty = IotDeviceProperty.builder()
                    .id(IdUtil.getSnowflakeNextId())
                    .projectId(projectId)
                    .deviceId(deviceId)
                    .propertyNo(partition.getPropertyNo())
                    .propertyName(partition.getPropertyName())
                    .propertyType(partition.getPropertyType())
                    .propertyUnit(partition.getPropertyUnit())
                    .propertyTable(tableName)
                    .tenantId("000000")
                    .status(SystemConstant.DB_STATUS_NORMAL)
                    .isDeleted(SystemConstant.DB_NOT_DELETED)
                    .createUserId(202110081530000L)
                    .createUserName("个人测试")
                    .createTime(now)
                    .updateUserId(202110081530000L)
                    .updateUserName("个人测试")
                    .updateTime(now)
                    .build();
            CreateDeviceDTO createDeviceDTO = CreateDeviceDTO.builder()
                    .propertyNo(partition.getPropertyNo())
                    .propertyType(partition.getPropertyType())
                    .build();
            if (DateTypeEnum.BINARY.getDateType().equalsIgnoreCase(partition.getPropertyType()) ||
                    DateTypeEnum.VARCHAR.getDateType().equalsIgnoreCase(partition.getPropertyType()) ||
                    DateTypeEnum.VARBINARY.getDateType().equalsIgnoreCase(partition.getPropertyType()) ||
                    DateTypeEnum.NCHAR.getDateType().equalsIgnoreCase(partition.getPropertyType())) {
                iotDeviceProperty.setPropertyScale(partition.getPropertyScale() == null ? 255 : partition.getPropertyScale());
                createDeviceDTO.setPropertyScale(partition.getPropertyScale() == null ? "255" : String.valueOf(partition.getPropertyScale()));
            } else if (DateTypeEnum.DECIMAL.getDateType().equalsIgnoreCase(partition.getPropertyType())) {
                iotDeviceProperty.setPropertyScale(null);
                createDeviceDTO.setPropertyScale("18, 6");
            } else {
                iotDeviceProperty.setPropertyScale(null);
                createDeviceDTO.setPropertyScale(null);
            }
            insertPropertyList.add(iotDeviceProperty);
            // 点位告警配置
            BigDecimal alarmValue = partition.getAlarmValue();
            if (alarmValue != null) {
                AlarmTypePropertyDTO alarmTypePropertyDTO = AlarmTypePropertyDTO.builder()
                        .gatewayNo(iotDevice.getGatewayNo())
                        .deviceId(deviceId)
                        .deviceNo(iotDevice.getDeviceNo())
                        .propertyId(iotDeviceProperty.getId())
                        .propertyNo(iotDeviceProperty.getPropertyNo())
                        .propertyName(iotDeviceProperty.getPropertyName())
                        .lowerLimit(alarmValue.toPlainString())
                        .upperLimit(alarmValue.toPlainString())
                        .noticeThreshold(60 * 60)
                        .build();
                IotAlarmConfig iotAlarmConfig = IotAlarmConfig.builder()
                        .alarmType(2)
                        .alarmName(partition.getPropertyName() + "告警")
                        .alarmNoticeType(1)
                        .alarmNoticeObject("")
                        .projectId(projectId)
                        .deviceId(iotDevice.getId())
                        .propertyId(iotDeviceProperty.getId())
                        .alarmConfig(JSONUtil.toJsonStr(alarmTypePropertyDTO))
                        .tenantId("000000")
                        .status(SystemConstant.DB_STATUS_NORMAL)
                        .isDeleted(SystemConstant.DB_NOT_DELETED)
                        .createUserId(202110081530000L)
                        .createUserName("个人测试")
                        .createTime(now)
                        .updateUserId(202110081530000L)
                        .updateUserName("个人测试")
                        .updateTime(now)
                        .build();
                insertAlarmConfigList.add(iotAlarmConfig);
            }
            try {
                // 动态数据源切换
                DynamicDataSourceContextHolder.push(SystemConstant.DS_TDENGINE);
                // 添加字段
                if (DateTypeEnum.BINARY.getDateType().equalsIgnoreCase(partition.getPropertyType()) ||
                        DateTypeEnum.VARCHAR.getDateType().equalsIgnoreCase(partition.getPropertyType()) ||
                        DateTypeEnum.VARBINARY.getDateType().equalsIgnoreCase(partition.getPropertyType()) ||
                        DateTypeEnum.NCHAR.getDateType().equalsIgnoreCase(partition.getPropertyType())) {
                    iotDatasourceMapper.addColumnNchar(tableName, partition.getPropertyNo(), partition.getPropertyType(), partition.getPropertyScale());
                } else if (DateTypeEnum.DECIMAL.getDateType().equalsIgnoreCase(partition.getPropertyType())) {
                    iotDatasourceMapper.addColumnDecimal(tableName, partition.getPropertyNo(), partition.getPropertyType(), 18, 6);
                } else {
                    iotDatasourceMapper.addColumn(tableName, partition.getPropertyNo(), partition.getPropertyType());
                }
            } catch (Exception ex) {
                log.error("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败", ex);
                throw new RuntimeException("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败");
            } finally {
                DynamicDataSourceContextHolder.clear();
            }
        }
        log.info("属性个数：{}，告警配置数;{}", insertPropertyList.size(), insertAlarmConfigList.size());
        // 属性入库
        iotDevicePropertyMapper.insert(insertPropertyList);
        // 属性告警配置入库
        iotAlarmConfigMapper.insert(insertAlarmConfigList);
        log.info("导入设备点位成功");
        return true;
    }

    @Override
    public List<DevicePropertyExportVO> exportDeviceProperty(Long deviceId) {
        return iotDevicePropertyMapper.exportDeviceProperty(deviceId);
    }

    @Override
    public boolean createAlarmConfig4AllDevice() {
        List<DeviceAlarmPropertyVO> deviceAlarmPropertyVOList = iotDevicePropertyMapper.selectDeviceAlarmPropertyList();
        if (CollectionUtils.isNotEmpty(deviceAlarmPropertyVOList)) {
            // 删除所有已经配置的告警
            iotAlarmConfigMapper.delete(Wrappers.lambdaQuery(IotAlarmConfig.class).le(IotAlarmConfig::getId, Long.MAX_VALUE));
            LocalDateTime now = LocalDateTime.now();
            List<IotAlarmConfig> insertAlarmConfigList = Lists.newArrayList();
            deviceAlarmPropertyVOList.forEach(deviceAlarmPropertyVO -> {
                Integer isAlarmConfig = deviceAlarmPropertyVO.getIsAlarmConfig();
                BigDecimal normalValue = deviceAlarmPropertyVO.getNormalValue();
                if (Integer.valueOf(1).equals(isAlarmConfig) && normalValue != null) {
                    AlarmTypePropertyDTO alarmTypePropertyDTO = AlarmTypePropertyDTO.builder()
                            .gatewayNo(deviceAlarmPropertyVO.getGatewayNo())
                            .deviceId(deviceAlarmPropertyVO.getDeviceId())
                            .deviceNo(deviceAlarmPropertyVO.getDeviceNo())
                            .propertyId(deviceAlarmPropertyVO.getPropertyId())
                            .propertyNo(deviceAlarmPropertyVO.getPropertyNo())
                            .propertyName(deviceAlarmPropertyVO.getPropertyName())
                            .lowerLimit(normalValue.toPlainString())
                            .upperLimit(normalValue.toPlainString())
                            .noticeThreshold(1 * 60 * 60)
                            .build();

                    IotAlarmConfig iotAlarmConfig = IotAlarmConfig.builder()
                            .alarmType(2)
                            .alarmName(deviceAlarmPropertyVO.getPropertyName() + "告警")
                            .alarmNoticeType(1)
                            .alarmNoticeObject("")
                            .projectId(deviceAlarmPropertyVO.getProjectId())
                            .deviceId(deviceAlarmPropertyVO.getDeviceId())
                            .propertyId(deviceAlarmPropertyVO.getPropertyId())
                            .alarmConfig(JSONUtil.toJsonStr(alarmTypePropertyDTO))
                            .tenantId(deviceAlarmPropertyVO.getTenantId())
                            .status(SystemConstant.DB_STATUS_NORMAL)
                            .isDeleted(SystemConstant.DB_NOT_DELETED)
                            .createUserId(deviceAlarmPropertyVO.getCreateUserId())
                            .createUserName(deviceAlarmPropertyVO.getCreateUserName())
                            .createTime(deviceAlarmPropertyVO.getCreateTime())
                            .updateUserId(deviceAlarmPropertyVO.getUpdateUserId())
                            .updateUserName(deviceAlarmPropertyVO.getUpdateUserName())
                            .updateTime(deviceAlarmPropertyVO.getUpdateTime())
                            .build();
                    insertAlarmConfigList.add(iotAlarmConfig);
                }
            });
            if (CollectionUtils.isNotEmpty(insertAlarmConfigList)) {
                iotAlarmConfigMapper.insert(insertAlarmConfigList);
            }
        } else {
            log.error("所有设备的属性都不需要生成告警配置");
        }
        return true;
    }

    @Override
    public List<QueryCurrentValueVO> queryCurrentValue(QueryCurrentValueDTO queryCurrentValueDTO) {
        IotDevice iotDevice = iotDeviceMapper.selectById(queryCurrentValueDTO.getDeviceId());
        if (null == iotDevice || SystemConstant.DB_IS_DELETED == iotDevice.getIsDeleted()) {
            throw new RuntimeException("当前设备不存在或已删除");
        }
        /*IotProject iotProject = iotProjectMapper.selectById(iotDevice.getProjectId());
        if (null == iotProject || SystemConstant.DB_IS_DELETED == iotProject.getIsDeleted()) {
            throw new RuntimeException("当前项目不存在或已删除");
        }
        if (null == iotProject.getDatasourceId() || 0L == iotProject.getDatasourceId()) {
            throw new RuntimeException("当前设备所属项目尚未绑定数据源，请先前往【项目管理】绑定数据源");
        }
        IotDatasource iotDatasource = iotDatasourceMapper.selectById(iotProject.getDatasourceId());
        if (iotDatasource.getDatabaseType() != 2) {
            throw new RuntimeException("请为当前项目绑定时序数据库");
        }*/
        List<IotDeviceProperty> propertyList = Lists.newArrayList();
        if (CollectionUtils.isEmpty(queryCurrentValueDTO.getPropertyIdList())) {
            // 查询所有
            propertyList.addAll(iotDevicePropertyMapper.selectList(
                    Wrappers.<IotDeviceProperty>query().lambda()
                            .eq(IotDeviceProperty::getDeviceId, queryCurrentValueDTO.getDeviceId())
                            .eq(IotDeviceProperty::getIsDeleted, SystemConstant.DB_NOT_DELETED)
            ));
        } else {
            // 查询选择的
            propertyList.addAll(iotDevicePropertyMapper.selectList(
                    Wrappers.<IotDeviceProperty>query().lambda()
                            .eq(IotDeviceProperty::getDeviceId, queryCurrentValueDTO.getDeviceId())
                            .in(IotDeviceProperty::getId, queryCurrentValueDTO.getPropertyIdList())
                            .eq(IotDeviceProperty::getIsDeleted, SystemConstant.DB_NOT_DELETED)
            ));
        }
        Map<String, List<IotDeviceProperty>> tableMap = propertyList.stream()
                .collect(Collectors.groupingBy(IotDeviceProperty::getPropertyTable));
        Map<String, Object> dataMap = Maps.newHashMap();
        try {
            // 动态数据源切换
            DynamicDataSourceContextHolder.push(SystemConstant.DS_TDENGINE);
            DynamicDataSourceContextHolder.peek();
            for (Map.Entry<String, List<IotDeviceProperty>> entry : tableMap.entrySet()) {
                String tableName = entry.getKey();
                List<IotDeviceProperty> devicePropertyList = entry.getValue();
                List<String> propertyNoList = devicePropertyList.stream()
                        .map(IotDeviceProperty::getPropertyNo)
                        .collect(Collectors.toList());
                Map<String, Object> tableData = iotDatasourceMapper.selectCurrentData(tableName, propertyNoList);
                if (null != tableData && !tableData.isEmpty()) {
                    dataMap.putAll(tableData);
                }
            }
        } catch (Exception ex) {
            log.error("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败", ex);
            throw new RuntimeException("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败");
        } finally {
            DynamicDataSourceContextHolder.clear();
        }
        List<QueryCurrentValueVO> result = Lists.newArrayList();
        propertyList.forEach(iotDeviceProperty -> {
            QueryCurrentValueVO queryCurrentValueVO = new QueryCurrentValueVO();
            queryCurrentValueVO.setPropertyId(iotDeviceProperty.getId());
            queryCurrentValueVO.setPropertyNo(iotDeviceProperty.getPropertyNo());
            queryCurrentValueVO.setPropertyName(iotDeviceProperty.getPropertyName());
            queryCurrentValueVO.setPropertyType(iotDeviceProperty.getPropertyType());
            queryCurrentValueVO.setPropertyUnit(iotDeviceProperty.getPropertyUnit());
            Object propertyValue = dataMap.get(iotDeviceProperty.getPropertyNo());
            queryCurrentValueVO.setPropertyValue(null == propertyValue ? "" : propertyValue);
            Object time = dataMap.get(iotDeviceProperty.getPropertyTable() + "_time");
            queryCurrentValueVO.setTime(null == time ? "" : String.valueOf(time));
            result.add(queryCurrentValueVO);
        });
        return result;
    }

    @Override
    public QueryHistoryValueVO queryHistoryValue(QueryHistoryValueDTO queryHistoryValueDTO) {
        IotDevice iotDevice = iotDeviceMapper.selectById(queryHistoryValueDTO.getDeviceId());
        if (null == iotDevice || SystemConstant.DB_IS_DELETED == iotDevice.getIsDeleted()) {
            throw new RuntimeException("当前设备不存在或已删除");
        }
        /*IotProject iotProject = iotProjectMapper.selectById(iotDevice.getProjectId());
        if (null == iotProject || SystemConstant.DB_IS_DELETED == iotProject.getIsDeleted()) {
            throw new RuntimeException("当前项目不存在或已删除");
        }
        if (null == iotProject.getDatasourceId() || 0L == iotProject.getDatasourceId()) {
            throw new RuntimeException("当前设备所属项目尚未绑定数据源，请先前往【项目管理】绑定数据源");
        }
        IotDatasource iotDatasource = iotDatasourceMapper.selectById(iotProject.getDatasourceId());
        if (iotDatasource.getDatabaseType() != 2) {
            throw new RuntimeException("请为当前项目绑定时序数据库");
        }*/
        List<IotDeviceProperty> propertyList = Lists.newArrayList();
        if (CollectionUtils.isEmpty(queryHistoryValueDTO.getPropertyIdList())) {
            // 查询所有
            propertyList.addAll(iotDevicePropertyMapper.selectList(
                    Wrappers.<IotDeviceProperty>query().lambda()
                            .eq(IotDeviceProperty::getDeviceId, queryHistoryValueDTO.getDeviceId())
                            .eq(IotDeviceProperty::getIsDeleted, SystemConstant.DB_NOT_DELETED)
            ));
        } else {
            // 查询选择的
            propertyList.addAll(iotDevicePropertyMapper.selectList(
                    Wrappers.<IotDeviceProperty>query().lambda()
                            .eq(IotDeviceProperty::getDeviceId, queryHistoryValueDTO.getDeviceId())
                            .in(IotDeviceProperty::getId, queryHistoryValueDTO.getPropertyIdList())
                            .eq(IotDeviceProperty::getIsDeleted, SystemConstant.DB_NOT_DELETED)
            ));
        }
        Map<String, List<IotDeviceProperty>> tableMap = propertyList.stream()
                .collect(Collectors.groupingBy(IotDeviceProperty::getPropertyTable));
        List<List<String>> tableHeader = Lists.newArrayList();
        List<List<String>> tableData = Lists.newArrayList();
        long current = 0, size = 0, total = 0, pages = 0;
        try {
            // 动态数据源切换
            DynamicDataSourceContextHolder.push(SystemConstant.DS_TDENGINE);
            DynamicDataSourceContextHolder.peek();
            for (Map.Entry<String, List<IotDeviceProperty>> entry : tableMap.entrySet()) {
                String tableName = entry.getKey();
                List<IotDeviceProperty> devicePropertyList = entry.getValue();
                List<String> propertyNoList = devicePropertyList.stream()
                        .map(IotDeviceProperty::getPropertyNo)
                        .collect(Collectors.toList());
                IPage<LinkedHashMap<String, Object>> singleTableDataPage = iotDatasourceMapper.selectHistoryData(new Page<>(queryHistoryValueDTO.getPage(), queryHistoryValueDTO.getSize()), tableName, propertyNoList, queryHistoryValueDTO.getStartTime(), queryHistoryValueDTO.getEndTime(), queryHistoryValueDTO.getSort());
                if (singleTableDataPage.getTotal() != 0) {
                    LinkedHashMap<String, Object> singleData = singleTableDataPage.getRecords().get(0);
                    singleData.keySet().forEach(cell -> {
                        if ("time".equalsIgnoreCase(cell)) {
                            tableHeader.add(Lists.newArrayList("时间"));
                        } else {
                            Optional<IotDeviceProperty> nameOptional = propertyList.stream()
                                    .filter(e -> e.getPropertyNo().equalsIgnoreCase(cell))
                                    .findAny();
                            if (nameOptional.isPresent()) {
                                tableHeader.add(Lists.newArrayList(nameOptional.get().getPropertyName()));
                            } else {
                                tableHeader.add(Lists.newArrayList(cell));
                            }
                        }
                    });
                    List<LinkedHashMap<String, Object>> records = singleTableDataPage.getRecords();
                    records.forEach(cellData -> tableData.add(cellData.values().stream()
                            .map(String::valueOf)
                            .collect(Collectors.toList())));
                    current = singleTableDataPage.getCurrent();
                    size = singleTableDataPage.getSize();
                    pages = singleTableDataPage.getPages();
                    total = singleTableDataPage.getTotal();
                }
            }
        } catch (Exception ex) {
            log.error("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败", ex);
            throw new RuntimeException("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败");
        } finally {
            DynamicDataSourceContextHolder.clear();
        }
        QueryHistoryValueVO queryHistoryValueVO = new QueryHistoryValueVO();
        queryHistoryValueVO.setTableHeader(tableHeader);
        Page<List<String>> tableDataPage = new Page<>();
        tableDataPage.setRecords(tableData);
        tableDataPage.setCurrent(current);
        tableDataPage.setSize(size);
        tableDataPage.setPages(pages);
        tableDataPage.setTotal(total);
        queryHistoryValueVO.setTableData(tableDataPage);
        return queryHistoryValueVO;
    }

    @Override
    public void exportHistoryValue(HttpServletResponse response, QueryHistoryValueDTO queryHistoryValueDTO) {
        IotDevice iotDevice = iotDeviceMapper.selectById(queryHistoryValueDTO.getDeviceId());
        if (null == iotDevice || SystemConstant.DB_IS_DELETED == iotDevice.getIsDeleted()) {
            throw new RuntimeException("当前设备不存在或已删除");
        }
        QueryHistoryValueVO queryHistoryValueVO = this.queryHistoryValue(queryHistoryValueDTO);
        try {
            response.setCharacterEncoding("utf-8");
            String fileName = iotDevice.getDeviceName() + "_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
            String excelFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + excelFileName + ".xlsx");
            EasyExcel.write(response.getOutputStream())
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .head(queryHistoryValueVO.getTableHeader())
                    .sheet(iotDevice.getDeviceName())
                    .doWrite(queryHistoryValueVO.getTableData().getRecords());
        } catch (IOException e) {
            throw new RuntimeException("导出失败");
        }
    }

    @Override
    public IPage<IotLog> logList(Page<IotLog> page, Long projectId, Long deviceId, String startTime, String endTime) {
        // 查询日志表
        String tableName = "log_" + projectId + "_" + deviceId;
        try {
            // 动态数据源切换
            DynamicDataSourceContextHolder.push(SystemConstant.DS_TDENGINE);
            DynamicDataSourceContextHolder.peek();
            return iotDatasourceMapper.selectLogList(page, tableName, startTime, endTime);
        } catch (Exception ex) {
            log.error("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败", ex);
            throw new RuntimeException("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败");
        } finally {
            DynamicDataSourceContextHolder.clear();
        }
    }

    @Override
    public Map<String, String> productCount(Long projectId) {
        Map<String, String> statusMap = iotDeviceMapper.selectDeviceStatus(projectId);
        return statusMap;
    }

    @Override
    public Map<String, String> deviceMsg(Long projectId, Long deviceId) {
        String tableName = "log_" + projectId + "_" + deviceId;
        LocalDate currentDate = LocalDate.now();
        // 定义日期格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // 当天的时间范围
        String startDate = currentDate.atStartOfDay().format(formatter);
        String todayEndStr = currentDate.atTime(23, 59, 59).format(formatter);
        // 最近一周的时间范围
        LocalDate lastWeekStart = currentDate.minusDays(6);
        LocalDate lastWeekEnd = currentDate;
        String startWithDate = lastWeekStart.atStartOfDay().format(formatter);
        String lastWeekEndStr = lastWeekEnd.atTime(23, 59, 59).format(formatter);
        // 本月的时间范围
        LocalDate firstDayOfMonth = currentDate.withDayOfMonth(1);
        LocalDate lastDayOfMonth = currentDate.withDayOfMonth(currentDate.lengthOfMonth());
        String startWithDayOfMonth = firstDayOfMonth.atStartOfDay().format(formatter);
        String lastDayOfMonthStr = lastDayOfMonth.atTime(23, 59, 59).format(formatter);
        try {
            // 动态数据源切换
            DynamicDataSourceContextHolder.push(SystemConstant.DS_TDENGINE);
            DynamicDataSourceContextHolder.peek();
            return iotDeviceMapper.selectMsg(tableName, startDate, todayEndStr, startWithDate, startWithDayOfMonth);
        } catch (Exception ex) {
            log.error("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败", ex);
            throw new RuntimeException("操作数据源【" + SystemConstant.DS_TDENGINE + "】失败");
        } finally {
            DynamicDataSourceContextHolder.clear();
        }
    }

    /**
     * 检查属性列表中是否存在重复的属性编号。如果存在重复的属性编号，则返回true；否则返回false。
     *
     * @param deviceNo     设备编号
     * @param propertyList 属性导入数据传输对象列表
     */
    private void hasDuplicatePropertyNo(String deviceNo, List<PropertyImportDTO> propertyList) {
        Set<String> seenPropertyNoSet = Sets.newHashSet();
        for (PropertyImportDTO property : propertyList) {
            if (!seenPropertyNoSet.add(property.getPropertyNo())) {
                throw new RuntimeException("上传的Excel表格中，设备编号为【" + deviceNo + "】，属性名称为【" + property.getPropertyName() + "】的属性编号【" + property.getPropertyNo() + "】存在重复");
            }
        }
    }

    private String convertPropertyType(String propertyType, String propertyScale) {
        return switch (propertyType) {
            case "1", "时间戳" -> "TIMESTAMP";
            case "2", "整型" -> "INT";
            case "4", "长整型" -> "BIGINT";
            case "6", "单精度浮点型" -> "FLOAT";
            case "7", "双精度浮点型" -> "DOUBLE";
            case "8", "单字节字符串" -> "BINARY (" + propertyScale + ")";
            case "9", "短整型" -> "SMALLINT";
            case "11", "单字节整型" -> "TINYINT";
            case "13", "布尔型" -> "BOOL";
            case "19", "高精度数值型" -> "DECIMAL(18, 6)";
            default -> "NCHAR (" + propertyScale + ")";
        };
    }

    private String convertPropertyTypeToCode(String propertyType) {
        return switch (propertyType) {
            case "1", "时间戳", "TIMESTAMP" -> "1";
            case "2", "整型", "INT" -> "2";
            case "4", "长整型", "BIGINT" -> "4";
            case "6", "单精度浮点型", "FLOAT" -> "6";
            case "7", "双精度浮点型", "DOUBLE" -> "7";
            case "8", "单字节字符串", "BINARY" -> "8";
            case "9", "短整型", "SMALLINT" -> "9";
            case "11", "单字节整型", "TINYINT" -> "11";
            case "13", "布尔型", "BOOL" -> "13";
            case "19", "高精度数值型", "DECIMAL" -> "19";
            default -> "14";
        };
    }
}
